﻿//using CSRedis;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Common.Framework.Core.Cache
{
    /// <summary>
    /// MemoryCache implementation
    /// </summary>
    public class MemoryCacheExtension : ICache
    {
        static readonly Microsoft.Extensions.Caching.Memory.MemoryCache cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new MemoryCacheOptions());

        /// <summary>
        /// 锁
        /// </summary>
        private readonly static object locker = new object();


        /// <summary>
        /// 添加缓存内容(滑动过期)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetSliding<TEntity>(string key, TEntity value, int expiresAtSecond)
        {
            if (key != null)
            {
                lock (locker)
                {
                    cache.Set<TEntity>(key, value, new MemoryCacheEntryOptions
                    {
                        SlidingExpiration = TimeSpan.FromSeconds(expiresAtSecond)
                    });
                }
            }
        }

        /// <summary>
        /// 添加缓存内容(定时过期)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Set<TEntity>(string key, TEntity value, DateTime expires)
        {
            if (key != null)
            {
                lock (locker)
                {
                    cache.Set<TEntity>(key, value, new MemoryCacheEntryOptions
                    {
                        AbsoluteExpiration = expires
                    });
                }
            }
        }

        /// <summary>
        /// 添加缓存内容(定时过期)
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiresAtSecond"></param>
        public void Set<TEntity>(string key, TEntity value, int expiresAtSecond)
        {
            if (key != null)
            {
                lock (locker)
                {
                    cache.Set<TEntity>(key, value, new MemoryCacheEntryOptions
                    {
                        AbsoluteExpiration = DateTime.Now.AddSeconds(expiresAtSecond)
                    });
                }
            }
        }

        /// <summary>
        /// 添加或获取缓存
        /// </summary>
        /// <typeparam name="T">泛型实体</typeparam>
        /// <param name="fuc">数据源方法</param>
        /// <param name="key">缓存key</param>
        /// <param name="cacheSeconds">缓存时间（秒）</param>
        /// <returns></returns>
        public TEntity Set<TEntity>(Func<TEntity> fuc, string key, int cacheSeconds)
        {
            var cacheValue = Get<TEntity>(key);
            if (cacheValue != null)
            {
                return cacheValue;
            }
            else
            {
                var fucValue = fuc();
                Set(key, fucValue, cacheSeconds);
                return fucValue;
            }
        }

        /// <summary>
        /// 清除缓存
        /// </summary>
        /// <param name="key">待清除的key</param>
        public void Remove(string key)
        {
            if (!string.IsNullOrWhiteSpace(key))
            {
                cache.Remove(key);
            }
        }

        /// <summary>
        /// 清除所有缓存
        /// </summary>
        public void Remove()
        {
            var list = GetCacheKeys();
            lock (locker)
            {
                foreach (var key in list)
                {
                    if (!key.Contains("CurrentApp"))
                    {
                        Remove(key);
                    }
                }
            }
        }


        /// <summary>
        /// 添加Hash表
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="keyValuePairs">待设置的值</param>
        /// <param name="expiresAtSecond">过期时间</param>
        public void SetHash<TEntity>(string key, IEnumerable<KeyValuePair<string, TEntity>> keyValuePairs, int expiresAtSecond)
        {
            Set<IEnumerable<KeyValuePair<string, TEntity>>>(key, keyValuePairs, expiresAtSecond);
        }

        /// <summary>
        /// 添加Hash表
        /// </summary>
        /// <typeparam name="TEntity">TEntity</typeparam>
        /// <param name="key">缓存Key</param>
        /// <param name="hashId">hashId</param>
        /// <param name="value">value</param>
        /// <param name="expiresAtSecond">过期时间</param>
        public void SetHash<TEntity>(string key, string hashId, TEntity value, int expiresAtSecond)
        {
            var list = Get<List<KeyValuePair<string, TEntity>>>(key);
            if (list == null)
            {
                list = new List<KeyValuePair<string, TEntity>>();
            }
            if (list.Any(it => it.Key == hashId))
            {
                list.Remove(list.FirstOrDefault(it => it.Key == hashId));
            }
            list.Add(new KeyValuePair<string, TEntity>(hashId, value));
            Set<IEnumerable<KeyValuePair<string, TEntity>>>(key, list, expiresAtSecond);
        }

        /// <summary>
        /// 添加Hash表
        /// </summary>
        /// <param name="fuc"></param>
        /// <param name="key">缓存Key</param>
        /// <param name="hashId">hashId</param>
        /// <param name="expiresAtSecond">过期时间</param
        public TEntity GetHash<TEntity>(Func<TEntity> fuc, string key, string hashId, int expiresAtSecond, bool isCache = true)
        {
            var cacheValue = GetHash<TEntity>(key, hashId);
            if (cacheValue != null && isCache)
            {
                return cacheValue;
            }
            else
            {
                if (fuc != null)
                {
                    var fucValue = fuc();
                    SetHash<TEntity>(key, hashId, fucValue, expiresAtSecond);
                    return fucValue;
                }
            }
            return default(TEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="key"></param>
        /// <param name="hashId"></param>
        /// <returns></returns>
        public TEntity GetHash<TEntity>(string key, string hashId)
        {
            var list = Get<IEnumerable<KeyValuePair<string, TEntity>>>(key);
            if (list == null)
            {
                return default(TEntity);
            }
            var item = list.FirstOrDefault(it => it.Key == hashId);
            if (item.Value != null)
            {
                return item.Value;
            }
            return default(TEntity);
        }

        /// <summary>
        /// 获取Hash表所有记录
        /// </summary>
        /// <typeparam name="TEntity">泛型</typeparam>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public List<TEntity> GetHash<TEntity>(string key)
        {
            var list = Get<IEnumerable<KeyValuePair<string, TEntity>>>(key);
            if (list == null)
            {
                return new List<TEntity>();
            }
            if (list.Any())
            {
                var result = new List<TEntity>();
                foreach (var item in list)
                {
                    result.Add(item.Value);
                }
                return result;
            }
            return new List<TEntity>();
        }

        /// <summary>
        /// 获取缓存值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TEntity Get<TEntity>(string key)
        {
            if (key != null && cache.TryGetValue(key, out TEntity val))
            {
                return val;
            }
            else
            {
                return default(TEntity);
            }
        }

        /// <summary>
        /// 添加或获取缓存
        /// </summary>
        /// <typeparam name="TEntity">泛型实体</typeparam>
        /// <param name="fuc">数据源方法</param>
        /// <param name="key">缓存key</param>
        /// <param name="expiresAtSecond">缓存时间（秒）</param>
        /// <param name="isCache">是否读取换存</param>
        /// <returns></returns>
        public TEntity Get<TEntity>(Func<TEntity> fuc, string key, int expiresAtSecond, bool isCache = true)
        {
            var cacheValue = Get<TEntity>(key);
            if (cacheValue != null && isCache)
            {
                return cacheValue;
            }
            else
            {
                if (fuc != null)
                {
                    var fucValue = fuc();
                    Set<TEntity>(key, fucValue, expiresAtSecond);
                    return fucValue;
                }
            }
            return default(TEntity);
        }

        /// <summary>
        /// 清除key以startsWithKey开头的缓存
        /// </summary>
        /// <param name="startsWithKey"></param>
        public void RemoveStartsWith(string startsWithKey)
        {
            var keys = GetCacheKeys();
            if (keys != null && keys.Any())
            {
                var count = keys.Count;
                for (int i = 0; i < count; i++)
                {
                    if (keys[i].StartsWith(startsWithKey))
                    {
                        Remove(keys[i]);
                    }
                }
            }
        }

        #region Distributed Locks

        ///// <summary>
        ///// 开启分布式锁，若超时返回null
        ///// </summary>
        ///// <param name="key">锁名称</param>
        ///// <param name="timeoutSeconds">超时（秒）</param>
        ///// <param name="autoDelay">自动延长锁超时时间，看门狗线程的超时时间为timeoutSeconds/2 ， 在看门狗线程超时时间时自动延长锁的时间为timeoutSeconds。除非程序意外退出，否则永不超时。</param>
        ///// <returns>true 锁定中 false 未锁定</returns>
        //public CSRedisClientLock LockTake(string key, int timeoutSeconds, bool autoDelay = false)
        //{
        //    return RedisHelper.Lock(key, timeoutSeconds, autoDelay);

        //}

        ///// <summary>
        ///// 释放锁
        ///// </summary>
        ///// <param name="cSRedisClientLock"></param>
        ///// <returns></returns>
        //public bool LockRelease(CSRedisClientLock cSRedisClientLock)
        //{
        //    if (cSRedisClientLock != null)
        //    {
        //        return cSRedisClientLock.Unlock();
        //    }
        //    return true;
        //}
        #endregion

        #region 私有方法
        /// <summary>
        /// 获取所有缓存键
        /// </summary>
        /// <returns></returns>
        private List<string> GetCacheKeys()
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = cache.GetType().GetField("_entries", flags).GetValue(cache);
            var keys = new List<string>();
            if (!(entries is IDictionary cacheItems)) return keys;
            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                keys.Add(cacheItem.Key.ToString());
            }
            return keys;
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public long RPush<TEntity>(string key, params TEntity[] value)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public string[] LRange(string key, long start, long stop = -1)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Task<long> RPushAsync<TEntity>(string key, params TEntity[] value)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public Task<string[]> LRangeAsync(string key, long start, long stop = -1)
        {
            throw new NotImplementedException();
        }

        public long SAdd<TEntity>(string key, int seconds, params TEntity[] members)
        {
            throw new NotImplementedException();
        }

        public string[] SMembers(string key, bool addprefix = false)
        {
            throw new NotImplementedException();
        }

        public Task<long> SAddAsync<TEntity>(string key, int seconds, params TEntity[] members)
        {
            throw new NotImplementedException();
        }

        public Task<string[]> SMembersAsync(string key, bool addprefix = false)
        {
            throw new NotImplementedException();
        }
    }
}
